﻿/**
 *@description:
 *@file: text_renderer.cpp
 *@author: snow-tyan
 *@email: zziywang@163.com
 *@date: 7 February 2023
 *@copyright: 2018-2022 AiRiA. All rights reserved.
 */

#include "mygl/text_renderer.h"
#include "mygl/shader.h"
#include "mygl/resource_manager.h"

#include <iostream>

#include <glm/gtc/matrix_transform.hpp>
#include <freetype2/ft2build.h>
#include FT_GLYPH_H

constexpr int kMaxShortValue = 1 << 16;

namespace mygl {

TextRenderer::TextRenderer(std::shared_ptr<Shader> shader, GLuint width, GLuint height)
    : shader_(shader) {
  glm::mat4 projection =
      glm::ortho(0.0f, static_cast<GLfloat>(width), 0.0f, static_cast<GLfloat>(height));
  shader_->UseProgram();
  shader_->SetUniformMat4Float("projection", projection);
  shader_->SetUniformInt("text", 0);
  initVertex();
}

void TextRenderer::Load(const std::string& font_path, GLuint font_size) {
  // 纹理默认是4字节对齐，这里改成1
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  FT_Library ft;
  if (FT_Init_FreeType(&ft)) {
    std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;
  }
  FT_Face face;
  if (FT_New_Face(ft, font_path.c_str(), 0, &face)) {
    std::cout << "ERROR::FREETYPE: Failed to load font: " << font_path << std::endl;
  }
  FT_Set_Pixel_Sizes(face, 0, font_size);
  // 存储128位ASCII
  for (GLint c = 0; c < 128; ++c) {
    if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
      std::cout << "ERROR::FREETYTPE: Failed to load Glyph" << std::endl;
      continue;
    }
    GLuint texture = initTexture(face->glyph->bitmap.width, face->glyph->bitmap.rows,
                                 face->glyph->bitmap.buffer);
    /* if (face->glyph->bitmap.buffer) {
       printf("c=%d, width=%d, height=%d, buffer=%d\n", c, face->glyph->bitmap.width,
              face->glyph->bitmap.rows, face->glyph->bitmap.buffer[0]);
     }*/
    Character character = {
        texture, glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
        glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
        face->glyph->advance.x};
    // 储存字符供之后使用
    characters_[c] = character;
  }
  glBindTexture(GL_TEXTURE_2D, GL_NONE);
  FT_Done_Face(face);
  FT_Done_FreeType(ft);
}

void TextRenderer::Render(const std::string& text, glm::vec2 position, GLfloat scale,
                          glm::vec3 color) {
  shader_->UseProgram();
  shader_->SetUniformVec3Float("text_color", color);
  glActiveTexture(GL_TEXTURE0);
  glBindVertexArray(vao_);

  for (const auto& c : text) {
    const auto& ch = characters_[c];
    // 计算字形四边形的左下角(xpos, ypos)和wh
    GLfloat xpos = position.x + ch.bearing.x * scale;
    GLfloat ypos = position.y - (ch.size.y - ch.bearing.y) * scale;
    GLfloat w = ch.size.x * scale;
    GLfloat h = ch.size.y * scale;
    // 对每个字符更新vbo
    GLfloat vertices[6][4] = {
        // position/texture
        {xpos, ypos + h, 0.0, 0.0},  //
        {xpos, ypos, 0.0, 1.0},      //
        {xpos + w, ypos, 1.0, 1.0},

        {xpos, ypos + h, 0.0, 0.0},  //
        {xpos + w, ypos, 1.0, 1.0},  //
        {xpos + w, ypos + h, 1.0, 0.0},
    };
    glBindTexture(GL_TEXTURE_2D, ch.texture_id);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    position.x += (ch.advance >> 6) * scale;  // 右移6位(2^6=64)来获取像素值(单位:1/64)
  }
  glBindVertexArray(GL_NONE);
  glBindTexture(GL_TEXTURE_2D, GL_NONE);
}

void TextRenderer::LoadChinese(const std::wstring& cn_text, const std::string& font_path,
                               GLuint font_size) {
  // 纹理默认是4字节对齐，这里改成1，确保位图不是4的整数倍也能正常渲染
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  FT_Library ft;
  if (FT_Init_FreeType(&ft)) {
    std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl;
  }
  FT_Face face;
  if (FT_New_Face(ft, font_path.c_str(), 0, &face)) {
    std::cout << "ERROR::FREETYPE: Failed to load font: " << font_path << std::endl;
  }
  FT_Set_Pixel_Sizes(face, 0, font_size);
  FT_Select_Charmap(face, ft_encoding_unicode);
  /**
   * FreeType 加载中文字符位图需要，先根据 Unicode
   * 编码值查询位图的索引，然后根据索引获取到 FreeType 的 Glyph 对象，最后再将 FT_Glyph
   * 转换为 FT_BitmapGlyph 获取到字体的位图。
   */
  for (size_t i = 0; i < cn_text.size(); ++i) {
    /// NOTE: FT_Load_Glyph
    if (FT_Load_Glyph(face, FT_Get_Char_Index(face, cn_text[i]), FT_LOAD_RENDER)) {
      std::cout << "ERROR::LOADCHINESE::FREETYTPE: Failed to load Glyph" << std::endl;
      continue;
    }
    FT_Glyph glyph;
    // 将face的字形移动到一个字形对象中。
    FT_Get_Glyph(face->glyph, &glyph);

    // Convert the glyph to a bitmap
    // FT_Render_Glyph(face->glyph, FT_RENDER_MODE_LCD);
    FT_Glyph_To_Bitmap(&glyph, ft_render_mode_normal, 0, 1);
    FT_BitmapGlyph bitmap_glyph = reinterpret_cast<FT_BitmapGlyph>(glyph);
    FT_Bitmap& bitmap = bitmap_glyph->bitmap;

    GLuint texture = initTexture(bitmap.width, bitmap.rows, bitmap.buffer);
    Character character = {
        texture,                                                        // 纹理ID
        glm::ivec2(bitmap.width, bitmap.rows),                          // 字形高宽
        glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),  // 字形方位
        // 水平预留值，右移16位，wchar是2个字节，左移6位是单位为1/64像素
        (glyph->advance.x >> 16) << 6};
    // 储存字符供之后使用
    characters_.insert(std::make_pair(cn_text[i], character));  // insert不会覆写
    // characters_[cn_text[i]] = character; // 下标会覆写更新
  }
  glBindTexture(GL_TEXTURE_2D, GL_NONE);
  FT_Done_Face(face);
  FT_Done_FreeType(ft);
}

void TextRenderer::RenderChinese(const std::wstring& cn_text, glm::vec2 position,
                                 GLfloat scale, glm::vec3 color) {
  shader_->UseProgram();
  shader_->SetUniformVec3Float("text_color", color);
  glActiveTexture(GL_TEXTURE0);
  glBindVertexArray(vao_);

  for (const auto& c : cn_text) {
    const auto& ch = characters_[c];
    // 计算字形四边形的左下角(xpos, ypos)和wh
    GLfloat xpos = position.x + ch.bearing.x * scale;
    GLfloat ypos = position.y - (ch.size.y - ch.bearing.y) * scale;
    GLfloat w = ch.size.x * scale;
    GLfloat h = ch.size.y * scale;
    // 对每个字符更新vbo
    GLfloat vertices[6][4] = {
        // position/texture
        {xpos, ypos + h, 0.0, 0.0},  //
        {xpos, ypos, 0.0, 1.0},      //
        {xpos + w, ypos, 1.0, 1.0},

        {xpos, ypos + h, 0.0, 0.0},  //
        {xpos + w, ypos, 1.0, 1.0},  //
        {xpos + w, ypos + h, 1.0, 0.0},
    };
    glBindTexture(GL_TEXTURE_2D, ch.texture_id);
    glBindBuffer(GL_ARRAY_BUFFER, vbo_);
    glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glDrawArrays(GL_TRIANGLES, 0, 6);
    position.x += (ch.advance >> 6) * scale;  // 右移6位(2^6=64)来获取像素值(单位:1/64)
  }
  glBindVertexArray(GL_NONE);
  glBindTexture(GL_TEXTURE_2D, GL_NONE);
}

void TextRenderer::initVertex() {
  glGenVertexArrays(1, &vao_);
  glGenBuffers(1, &vbo_);
  glBindVertexArray(vao_);
  glBindBuffer(GL_ARRAY_BUFFER, vbo_);
  // 每个四边形需要6个顶点，每个顶点又是由一个4float向量(顶点/纹理)
  // 绘制字符时经常更新VBO的内存，内存类型设置为GL_DYNAMIC_DRAW。
  glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 6 * 4, nullptr, GL_DYNAMIC_DRAW);
  glEnableVertexAttribArray(0);
  glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void*)0);
  glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
  glBindVertexArray(GL_NONE);
}

GLuint TextRenderer::initTexture(GLuint font_width, GLuint font_height, void* buffer) {
  GLuint texture;
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
#ifdef GL_ES
  glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, font_width, font_height, 0, GL_LUMINANCE,
               GL_UNSIGNED_BYTE, buffer);
#else
  glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, font_width, font_height, 0, GL_RED,
               GL_UNSIGNED_BYTE, buffer);
#endif
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  return texture;
}

}  // namespace mygl
